Ext.tree.TreeCheckNodeUI = function() {
  //only leaf node has checkbox
  this.onlyLeafCheckable = false;
    
  Ext.tree.TreeCheckNodeUI.superclass.constructor.apply(this, arguments);
};

Ext.extend(Ext.tree.TreeCheckNodeUI, Ext.tree.TreeNodeUI, {

  renderElements : function(n, a, targetNode, bulkRender) {
    var tree = n.getOwnerTree();
    this.onlyLeafCheckable = tree.onlyLeafCheckable || false;
    
    // add some indent caching, this helps performance when rendering a large tree
    this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';

    var href = a.href ? a.href : Ext.isGecko ? "" : "#";

    var buf = ['<li class="x-tree-node"><div ext:tree-node-id="', n.id,
        '" class="x-tree-node-el x-tree-node-leaf x-unselectable ', a.cls, 
        '" unselectable="on">','<span class="x-tree-node-indent">', this.indentMarkup, "</span>",
        '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow" />',
        '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon', 
        (a.icon ? " x-tree-node-inline-icon" : ""), 
        (a.iconCls ? " " + a.iconCls : ""), '" unselectable="on" />',
        '<img class="ext-tree-cb', 
        (a.checkedState == 2 || a.checkedState == 1 ? (a.checkedState == 2 ? ' ext-cb-checked' : ' ext-cb-partial') : ' ext-cb-unchecked'), 
        '" src="', this.emptyIcon, '" />',
        '<a hidefocus="on" class="x-tree-node-anchor" href="', 
        href, '" tabIndex="1" ', (a.hrefTarget ? ' target="' + a.hrefTarget + '"' : ""), 
        '><span unselectable="on">', n.text, "</span></a></div>",
        '<ul class="x-tree-node-ct" style="display:none;"></ul>',
        "</li>"].join('');
    var nel;

    if(bulkRender !== true && n.nextSibling && (nel = n.nextSibling.ui.getEl())){
      this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
    }
    else {
      this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
    }
    
    this.elNode = this.wrap.childNodes[0];
    this.ctNode = this.wrap.childNodes[1];
    var cs = this.elNode.childNodes;
    this.indentNode = cs[0];
    this.ecNode = cs[1];
    this.iconNode = cs[2];
    var index = 3;

    this.checkbox = cs[3];
    Ext.fly(this.checkbox).on('click', this.check.createDelegate(this,[null]));
    index++;

    this.anchor = cs[index];
    this.textNode = cs[index].firstChild;
  },
  
  // private
  check : function(forceState){
    var n = this.node;
    if(forceState === null) {
      var state = ((n.attributes.checkedState < 2) ? 2 : 0);
    }
    else {
      var state = forceState;
    }
    
    this.changeCheckState(n, state); 
    
      var parentNode = n.parentNode;
      if(forceState === null && parentNode !== null) {
        this.parentCheck(parentNode, n.attributes.checkedState); //bubble
      }

      if(state == 1) return;
      if(!n.expanded && !n.childrenRendered) {
        var changeChildrenState = function(children, s) {
          for(var i in children) {
            if(children[i].children) changeChildrenState(children[i].children, s);
            children[i].checkedState = s;
          }
        }
        changeChildrenState(n.attributes.children, state);
      }
      else {
        this.childCheck(n);
      }
    n.fireEvent('checkchange');
  },
  
  // private
  childCheck : function(node){
    var a = node.attributes;

    if(!a.leaf) {
      var cs = node.childNodes;
      for(var i = 0; i < cs.length; i++) {
        cs[i].getUI().check(a.checkedState); // cascade
      }
    }
  },
  
  // private
  parentCheck : function(node, checked){
    var checkbox = Ext.fly(node.getUI().checkbox);
    this.changeCheckState(node, this.childHasChecked(node));
    node.getOwnerTree().fireEvent('check', node, checked);
    
    var parentNode = node.parentNode;
    if( parentNode !== null) { // bubble
      this.parentCheck(parentNode, checked); 
    }
  },
  
  // private
  childHasChecked : function(node){
    var childNodes = node.childNodes;
    var childChecked = 0; 
    if(childNodes || childNodes.length > 0){
      for(var i = 0; i < childNodes.length; i++){
        if(childNodes[i].attributes.checkedState == 2) {
          childChecked++;
        }
        else if(childNodes[i].attributes.checkedState == 1) {
          return 1; // at least 1 child partially checked, short circuit 
        }
      }
    }
    if(childChecked == childNodes.length) return 2; // all childs fully checked
    else if(childChecked > 0) return 1; // some childs checked
    return 0; // no childs checked
  },

  //private
  // state 2 = fully checked, 1 = partial, 0 = unchecked
  changeCheckState: function(node, state) {
    var currentState = node.attributes.checkedState;
    var cb = Ext.fly(node.getUI().checkbox);
    if(currentState == state) return;
    if(cb) {
      if(currentState == 2) cb.removeClass('ext-cb-checked');
      else if(currentState == 1) cb.removeClass('ext-cb-partial');
      else cb.removeClass('ext-cb-unchecked');

      if(state == 2) cb.addClass('ext-cb-checked');
      else if(state == 1) cb.addClass('ext-cb-partial');
      else cb.addClass('ext-cb-unchecked');
    }
    node.attributes.checkedState = state;
    node.fireEvent('checkchange', node, state);
  }
});

